<h1 id="拖拽">拖拽</h1>
<p>在GUI里，拖放是指用户点击一个虚拟的对象，拖动，然后放置到另外一个对象上面的动作。一般情况下，需要调用很多动作和方法，创建很多变量。</p>
<p>拖放能让用户很直观的操作很复杂的逻辑。</p>
<p>一般情况下，我们可以拖放两种东西：数据和图形界面。把一个图像从一个应用拖放到另外一个应用上的实质是操作二进制数据。把一个表格从Firefox上拖放到另外一个位置 的实质是操作一个图形组。</p>
<h2 id="简单的拖放">简单的拖放</h2>
<p>本例使用了<code>QLineEdit</code>和<code>QPushButton</code>。把一个文本从编辑框里拖到按钮上，更新按钮上的标签（文字）。</p>
<div class="sourceCode" id="cb1"><pre><code class="language-python"><a class="sourceLine" id="cb1-1" data-line-number="1"><span class="co">#!/usr/bin/python3</span></a>
<a class="sourceLine" id="cb1-2" data-line-number="2"><span class="co"># -*- coding: utf-8 -*-</span></a>
<a class="sourceLine" id="cb1-3" data-line-number="3"></a>
<a class="sourceLine" id="cb1-4" data-line-number="4"><span class="co">&quot;&quot;&quot;</span></a>
<a class="sourceLine" id="cb1-5" data-line-number="5"><span class="co">ZetCode PyQt5 tutorial</span></a>
<a class="sourceLine" id="cb1-6" data-line-number="6"></a>
<a class="sourceLine" id="cb1-7" data-line-number="7"><span class="co">This is a simple drag and</span></a>
<a class="sourceLine" id="cb1-8" data-line-number="8"><span class="co">drop example. </span></a>
<a class="sourceLine" id="cb1-9" data-line-number="9"></a>
<a class="sourceLine" id="cb1-10" data-line-number="10"><span class="co">Author: Jan Bodnar</span></a>
<a class="sourceLine" id="cb1-11" data-line-number="11"><span class="co">Website: zetcode.com</span></a>
<a class="sourceLine" id="cb1-12" data-line-number="12"><span class="co">Last edited: August 2017</span></a>
<a class="sourceLine" id="cb1-13" data-line-number="13"><span class="co">&quot;&quot;&quot;</span></a>
<a class="sourceLine" id="cb1-14" data-line-number="14"></a>
<a class="sourceLine" id="cb1-15" data-line-number="15"><span class="im">from</span> PyQt5.QtWidgets <span class="im">import</span> (QPushButton, QWidget, </a>
<a class="sourceLine" id="cb1-16" data-line-number="16">    QLineEdit, QApplication)</a>
<a class="sourceLine" id="cb1-17" data-line-number="17"><span class="im">import</span> sys</a>
<a class="sourceLine" id="cb1-18" data-line-number="18"></a>
<a class="sourceLine" id="cb1-19" data-line-number="19"><span class="kw">class</span> Button(QPushButton):</a>
<a class="sourceLine" id="cb1-20" data-line-number="20">  </a>
<a class="sourceLine" id="cb1-21" data-line-number="21">    <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, title, parent):</a>
<a class="sourceLine" id="cb1-22" data-line-number="22">        <span class="bu">super</span>().<span class="fu">__init__</span>(title, parent)</a>
<a class="sourceLine" id="cb1-23" data-line-number="23">        </a>
<a class="sourceLine" id="cb1-24" data-line-number="24">        <span class="va">self</span>.setAcceptDrops(<span class="va">True</span>)</a>
<a class="sourceLine" id="cb1-25" data-line-number="25">        </a>
<a class="sourceLine" id="cb1-26" data-line-number="26"></a>
<a class="sourceLine" id="cb1-27" data-line-number="27">    <span class="kw">def</span> dragEnterEvent(<span class="va">self</span>, e):</a>
<a class="sourceLine" id="cb1-28" data-line-number="28">      </a>
<a class="sourceLine" id="cb1-29" data-line-number="29">        <span class="cf">if</span> e.mimeData().hasFormat(<span class="st">&#39;text/plain&#39;</span>):</a>
<a class="sourceLine" id="cb1-30" data-line-number="30">            e.accept()</a>
<a class="sourceLine" id="cb1-31" data-line-number="31">        <span class="cf">else</span>:</a>
<a class="sourceLine" id="cb1-32" data-line-number="32">            e.ignore() </a>
<a class="sourceLine" id="cb1-33" data-line-number="33"></a>
<a class="sourceLine" id="cb1-34" data-line-number="34">    <span class="kw">def</span> dropEvent(<span class="va">self</span>, e):</a>
<a class="sourceLine" id="cb1-35" data-line-number="35">        </a>
<a class="sourceLine" id="cb1-36" data-line-number="36">        <span class="va">self</span>.setText(e.mimeData().text()) </a>
<a class="sourceLine" id="cb1-37" data-line-number="37"></a>
<a class="sourceLine" id="cb1-38" data-line-number="38"></a>
<a class="sourceLine" id="cb1-39" data-line-number="39"><span class="kw">class</span> Example(QWidget):</a>
<a class="sourceLine" id="cb1-40" data-line-number="40">  </a>
<a class="sourceLine" id="cb1-41" data-line-number="41">    <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-42" data-line-number="42">        <span class="bu">super</span>().<span class="fu">__init__</span>()</a>
<a class="sourceLine" id="cb1-43" data-line-number="43">        </a>
<a class="sourceLine" id="cb1-44" data-line-number="44">        <span class="va">self</span>.initUI()</a>
<a class="sourceLine" id="cb1-45" data-line-number="45">        </a>
<a class="sourceLine" id="cb1-46" data-line-number="46">        </a>
<a class="sourceLine" id="cb1-47" data-line-number="47">    <span class="kw">def</span> initUI(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-48" data-line-number="48"></a>
<a class="sourceLine" id="cb1-49" data-line-number="49">        edit <span class="op">=</span> QLineEdit(<span class="st">&#39;&#39;</span>, <span class="va">self</span>)</a>
<a class="sourceLine" id="cb1-50" data-line-number="50">        edit.setDragEnabled(<span class="va">True</span>)</a>
<a class="sourceLine" id="cb1-51" data-line-number="51">        edit.move(<span class="dv">30</span>, <span class="dv">65</span>)</a>
<a class="sourceLine" id="cb1-52" data-line-number="52"></a>
<a class="sourceLine" id="cb1-53" data-line-number="53">        button <span class="op">=</span> Button(<span class="st">&quot;Button&quot;</span>, <span class="va">self</span>)</a>
<a class="sourceLine" id="cb1-54" data-line-number="54">        button.move(<span class="dv">190</span>, <span class="dv">65</span>)</a>
<a class="sourceLine" id="cb1-55" data-line-number="55">        </a>
<a class="sourceLine" id="cb1-56" data-line-number="56">        <span class="va">self</span>.setWindowTitle(<span class="st">&#39;Simple drag and drop&#39;</span>)</a>
<a class="sourceLine" id="cb1-57" data-line-number="57">        <span class="va">self</span>.setGeometry(<span class="dv">300</span>, <span class="dv">300</span>, <span class="dv">300</span>, <span class="dv">150</span>)</a>
<a class="sourceLine" id="cb1-58" data-line-number="58"></a>
<a class="sourceLine" id="cb1-59" data-line-number="59"></a>
<a class="sourceLine" id="cb1-60" data-line-number="60"><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&#39;__main__&#39;</span>:</a>
<a class="sourceLine" id="cb1-61" data-line-number="61">  </a>
<a class="sourceLine" id="cb1-62" data-line-number="62">    app <span class="op">=</span> QApplication(sys.argv)</a>
<a class="sourceLine" id="cb1-63" data-line-number="63">    ex <span class="op">=</span> Example()</a>
<a class="sourceLine" id="cb1-64" data-line-number="64">    ex.show()</a>
<a class="sourceLine" id="cb1-65" data-line-number="65">    app.exec_()</a></code></pre></div>
<pre><code class="language-python">class Button(QPushButton):
  
    def __init__(self, title, parent):
        super().__init__(title, parent)
        
        self.setAcceptDrops(True)</code></pre>
<p>为了完成预定目标，我们要重构一些方法。首先用<code>QPushButton</code>上构造一个按钮实例。</p>
<pre><code class="language-python">self.setAcceptDrops(True)</code></pre>
<p>激活组件的拖拽事件。</p>
<pre><code class="language-python">def dragEnterEvent(self, e):
    
    if e.mimeData().hasFormat(&#39;text/plain&#39;):
        e.accept()
    else:
        e.ignore() </code></pre>
<p>首先，我们重构了<code>dragEnterEvent()</code>方法。设定好接受拖拽的数据类型（plain text）。</p>
<pre><code class="language-python">def dropEvent(self, e):

    self.setText(e.mimeData().text()) </code></pre>
<p>然后重构<code>dropEvent()</code>方法，更改按钮接受鼠标的释放事件的默认行为。</p>
<pre><code class="language-python">edit = QLineEdit(&#39;&#39;, self)
edit.setDragEnabled(True)</code></pre>
<p><code>QLineEdit</code>默认支持拖拽操作，所以我们只要调用<code>setDragEnabled()</code>方法使用就行了。</p>
<p>程序展示：</p>
<figure>
<img class="whitelist" src="docs/PyQt5/images/8-dragdrop.png" alt="drag &amp; drop" />
</figure>
<h2 id="拖放按钮组件">拖放按钮组件</h2>
<p>这个例子展示怎么拖放一个button组件。</p>
<div class="sourceCode" id="cb7"><pre><code class="language-python"><a class="sourceLine" id="cb7-1" data-line-number="1"><span class="co">#!/usr/bin/python3</span></a>
<a class="sourceLine" id="cb7-2" data-line-number="2"><span class="co"># -*- coding: utf-8 -*-</span></a>
<a class="sourceLine" id="cb7-3" data-line-number="3"></a>
<a class="sourceLine" id="cb7-4" data-line-number="4"><span class="co">&quot;&quot;&quot;</span></a>
<a class="sourceLine" id="cb7-5" data-line-number="5"><span class="co">ZetCode PyQt5 tutorial</span></a>
<a class="sourceLine" id="cb7-6" data-line-number="6"></a>
<a class="sourceLine" id="cb7-7" data-line-number="7"><span class="co">In this program, we can press on a button with a left mouse</span></a>
<a class="sourceLine" id="cb7-8" data-line-number="8"><span class="co">click or drag and drop the button with  the right mouse click. </span></a>
<a class="sourceLine" id="cb7-9" data-line-number="9"></a>
<a class="sourceLine" id="cb7-10" data-line-number="10"><span class="co">Author: Jan Bodnar</span></a>
<a class="sourceLine" id="cb7-11" data-line-number="11"><span class="co">Website: zetcode.com</span></a>
<a class="sourceLine" id="cb7-12" data-line-number="12"><span class="co">Last edited: August 2017</span></a>
<a class="sourceLine" id="cb7-13" data-line-number="13"><span class="co">&quot;&quot;&quot;</span></a>
<a class="sourceLine" id="cb7-14" data-line-number="14"></a>
<a class="sourceLine" id="cb7-15" data-line-number="15"><span class="im">from</span> PyQt5.QtWidgets <span class="im">import</span> QPushButton, QWidget, QApplication</a>
<a class="sourceLine" id="cb7-16" data-line-number="16"><span class="im">from</span> PyQt5.QtCore <span class="im">import</span> Qt, QMimeData</a>
<a class="sourceLine" id="cb7-17" data-line-number="17"><span class="im">from</span> PyQt5.QtGui <span class="im">import</span> QDrag</a>
<a class="sourceLine" id="cb7-18" data-line-number="18"><span class="im">import</span> sys</a>
<a class="sourceLine" id="cb7-19" data-line-number="19"></a>
<a class="sourceLine" id="cb7-20" data-line-number="20"><span class="kw">class</span> Button(QPushButton):</a>
<a class="sourceLine" id="cb7-21" data-line-number="21">  </a>
<a class="sourceLine" id="cb7-22" data-line-number="22">    <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, title, parent):</a>
<a class="sourceLine" id="cb7-23" data-line-number="23">        <span class="bu">super</span>().<span class="fu">__init__</span>(title, parent)</a>
<a class="sourceLine" id="cb7-24" data-line-number="24">        </a>
<a class="sourceLine" id="cb7-25" data-line-number="25"></a>
<a class="sourceLine" id="cb7-26" data-line-number="26">    <span class="kw">def</span> mouseMoveEvent(<span class="va">self</span>, e):</a>
<a class="sourceLine" id="cb7-27" data-line-number="27"></a>
<a class="sourceLine" id="cb7-28" data-line-number="28">        <span class="cf">if</span> e.buttons() <span class="op">!=</span> Qt.RightButton:</a>
<a class="sourceLine" id="cb7-29" data-line-number="29">            <span class="cf">return</span></a>
<a class="sourceLine" id="cb7-30" data-line-number="30"></a>
<a class="sourceLine" id="cb7-31" data-line-number="31">        mimeData <span class="op">=</span> QMimeData()</a>
<a class="sourceLine" id="cb7-32" data-line-number="32"></a>
<a class="sourceLine" id="cb7-33" data-line-number="33">        drag <span class="op">=</span> QDrag(<span class="va">self</span>)</a>
<a class="sourceLine" id="cb7-34" data-line-number="34">        drag.setMimeData(mimeData)</a>
<a class="sourceLine" id="cb7-35" data-line-number="35">        drag.setHotSpot(e.pos() <span class="op">-</span> <span class="va">self</span>.rect().topLeft())</a>
<a class="sourceLine" id="cb7-36" data-line-number="36"></a>
<a class="sourceLine" id="cb7-37" data-line-number="37">        dropAction <span class="op">=</span> drag.exec_(Qt.MoveAction)</a>
<a class="sourceLine" id="cb7-38" data-line-number="38"></a>
<a class="sourceLine" id="cb7-39" data-line-number="39"></a>
<a class="sourceLine" id="cb7-40" data-line-number="40">    <span class="kw">def</span> mousePressEvent(<span class="va">self</span>, e):</a>
<a class="sourceLine" id="cb7-41" data-line-number="41">      </a>
<a class="sourceLine" id="cb7-42" data-line-number="42">        <span class="bu">super</span>().mousePressEvent(e)</a>
<a class="sourceLine" id="cb7-43" data-line-number="43">        </a>
<a class="sourceLine" id="cb7-44" data-line-number="44">        <span class="cf">if</span> e.button() <span class="op">==</span> Qt.LeftButton:</a>
<a class="sourceLine" id="cb7-45" data-line-number="45">            <span class="bu">print</span>(<span class="st">&#39;press&#39;</span>)</a>
<a class="sourceLine" id="cb7-46" data-line-number="46"></a>
<a class="sourceLine" id="cb7-47" data-line-number="47"></a>
<a class="sourceLine" id="cb7-48" data-line-number="48"><span class="kw">class</span> Example(QWidget):</a>
<a class="sourceLine" id="cb7-49" data-line-number="49">  </a>
<a class="sourceLine" id="cb7-50" data-line-number="50">    <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb7-51" data-line-number="51">        <span class="bu">super</span>().<span class="fu">__init__</span>()</a>
<a class="sourceLine" id="cb7-52" data-line-number="52"></a>
<a class="sourceLine" id="cb7-53" data-line-number="53">        <span class="va">self</span>.initUI()</a>
<a class="sourceLine" id="cb7-54" data-line-number="54">        </a>
<a class="sourceLine" id="cb7-55" data-line-number="55">        </a>
<a class="sourceLine" id="cb7-56" data-line-number="56">    <span class="kw">def</span> initUI(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb7-57" data-line-number="57"></a>
<a class="sourceLine" id="cb7-58" data-line-number="58">        <span class="va">self</span>.setAcceptDrops(<span class="va">True</span>)</a>
<a class="sourceLine" id="cb7-59" data-line-number="59"></a>
<a class="sourceLine" id="cb7-60" data-line-number="60">        <span class="va">self</span>.button <span class="op">=</span> Button(<span class="st">&#39;Button&#39;</span>, <span class="va">self</span>)</a>
<a class="sourceLine" id="cb7-61" data-line-number="61">        <span class="va">self</span>.button.move(<span class="dv">100</span>, <span class="dv">65</span>)</a>
<a class="sourceLine" id="cb7-62" data-line-number="62"></a>
<a class="sourceLine" id="cb7-63" data-line-number="63">        <span class="va">self</span>.setWindowTitle(<span class="st">&#39;Click or Move&#39;</span>)</a>
<a class="sourceLine" id="cb7-64" data-line-number="64">        <span class="va">self</span>.setGeometry(<span class="dv">300</span>, <span class="dv">300</span>, <span class="dv">280</span>, <span class="dv">150</span>)</a>
<a class="sourceLine" id="cb7-65" data-line-number="65">        </a>
<a class="sourceLine" id="cb7-66" data-line-number="66"></a>
<a class="sourceLine" id="cb7-67" data-line-number="67">    <span class="kw">def</span> dragEnterEvent(<span class="va">self</span>, e):</a>
<a class="sourceLine" id="cb7-68" data-line-number="68">      </a>
<a class="sourceLine" id="cb7-69" data-line-number="69">        e.accept()</a>
<a class="sourceLine" id="cb7-70" data-line-number="70">        </a>
<a class="sourceLine" id="cb7-71" data-line-number="71"></a>
<a class="sourceLine" id="cb7-72" data-line-number="72">    <span class="kw">def</span> dropEvent(<span class="va">self</span>, e):</a>
<a class="sourceLine" id="cb7-73" data-line-number="73"></a>
<a class="sourceLine" id="cb7-74" data-line-number="74">        position <span class="op">=</span> e.pos()</a>
<a class="sourceLine" id="cb7-75" data-line-number="75">        <span class="va">self</span>.button.move(position)</a>
<a class="sourceLine" id="cb7-76" data-line-number="76"></a>
<a class="sourceLine" id="cb7-77" data-line-number="77">        e.setDropAction(Qt.MoveAction)</a>
<a class="sourceLine" id="cb7-78" data-line-number="78">        e.accept()</a>
<a class="sourceLine" id="cb7-79" data-line-number="79">        </a>
<a class="sourceLine" id="cb7-80" data-line-number="80"></a>
<a class="sourceLine" id="cb7-81" data-line-number="81"><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&#39;__main__&#39;</span>:</a>
<a class="sourceLine" id="cb7-82" data-line-number="82">  </a>
<a class="sourceLine" id="cb7-83" data-line-number="83">    app <span class="op">=</span> QApplication(sys.argv)</a>
<a class="sourceLine" id="cb7-84" data-line-number="84">    ex <span class="op">=</span> Example()</a>
<a class="sourceLine" id="cb7-85" data-line-number="85">    ex.show()</a>
<a class="sourceLine" id="cb7-86" data-line-number="86">    app.exec_()</a></code></pre></div>
<p>上面的例子中，窗口上有一个<code>QPushButton</code>组件。左键点击按钮，控制台就会输出<code>press</code>。右键可以点击然后拖动按钮。</p>
<pre><code class="language-python">class Button(QPushButton):
  
    def __init__(self, title, parent):
        super().__init__(title, parent)</code></pre>
<p>从<code>QPushButton</code>继承一个<code>Button</code>类，然后重构<code>QPushButton</code>的两个方法:<code>mouseMoveEvent()</code>和<code>mousePressEvent()</code>.<code>mouseMoveEvent()</code>是拖拽开始的事件。</p>
<pre><code class="language-python">if e.buttons() != Qt.RightButton:
    return</code></pre>
<p>我们只劫持按钮的右键事件，左键的操作还是默认行为。</p>
<pre><code class="language-python">mimeData = QMimeData()

drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.pos() - self.rect().topLeft())</code></pre>
<p>创建一个<code>QDrag</code>对象，用来传输MIME-based数据。</p>
<pre><code class="language-python">dropAction = drag.exec_(Qt.MoveAction)</code></pre>
<p>拖放事件开始时，用到的处理函数式<code>start()</code>.</p>
<pre><code class="language-python">def mousePressEvent(self, e):
    
    QPushButton.mousePressEvent(self, e)
    
    if e.button() == Qt.LeftButton:
        print(&#39;press&#39;)</code></pre>
<p>左键点击按钮，会在控制台输出“press”。注意，我们在父级上也调用了<code>mousePressEvent()</code>方法，不然的话，我们是看不到按钮按下的效果的。</p>
<pre><code class="language-python">position = e.pos()
self.button.move(position)</code></pre>
<p>在<code>dropEvent()</code>方法里，我们定义了按钮按下后和释放后的行为，获得鼠标移动的位置，然后把按钮放到这个地方。</p>
<pre><code class="language-python">e.setDropAction(Qt.MoveAction)
e.accept()</code></pre>
<p>指定放下的动作类型为moveAction。</p>
<p>程序展示：</p>
<p>这个就一个按钮，没啥可展示的，弄GIF太麻烦了。</p>
